package org.yunai.swjg.server.core.message.codec;

import com.alibaba.fastjson.JSON;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.yunai.swjg.server.rpc.MessageManager;
import org.yunai.swjg.server.util.TGWUtil;
import org.yunai.yfserver.message.AbstractDecoder;
import org.yunai.yfserver.message.ByteArray;
import org.yunai.yfserver.message.IStruct;

/**
 * 游戏消息解码器
 * User: yunai
 * Date: 13-1-30
 * Time: 下午3:56
 */
public class GameMessageDecoder implements ProtocolDecoder {

    private final AttributeKey BUFFER = new AttributeKey(getClass(), "buffer");

    @Override
    public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        boolean usingSessionBuffer = true;
        IoBuffer buf = (IoBuffer) session.getAttribute(BUFFER);
        // If we have a session buffer, append data to that; otherwise
        // use the buffer read from the network directly.
        if (buf != null) {
            boolean appended = false;
            // Make sure that the buffer is auto-expanded.
            if (buf.isAutoExpand()) {
                try {
                    buf.put(in);
                    appended = true;
                } catch (IllegalStateException e) {
                    // A user called derivation method (e.g. slice()),
                    // which disables auto-expansion of the parent buffer.
                } catch (IndexOutOfBoundsException e) {
                    // A user disabled auto-expansion.
                }
            }

            if (appended) {
                buf.flip();
            } else {
                // Reallocate the buffer if append operation failed due to
                // derivation or disabled auto-expansion.
                buf.flip();
                IoBuffer newBuf = IoBuffer.allocate(buf.remaining() + in.remaining()).setAutoExpand(true);
                newBuf.order(buf.order());
                newBuf.put(buf);
                newBuf.put(in);
                newBuf.flip();
                buf = newBuf;

                // Update the session attribute.
                session.setAttribute(BUFFER, buf);
            }
        } else {
            buf = in;
            usingSessionBuffer = false;
        }

        TGWUtil.tgwFilter(buf.buf());

        for (;;) {
            if (!doDecode(session, buf, out) || !buf.hasRemaining()) {
                break;
            }
        }

        // if there is any data left that cannot be decoded, we store
        // it in a buffer in the session and next time this decoder is
        // invoked the session buffer gets appended to
        if (buf.hasRemaining()) {
            if (usingSessionBuffer && buf.isAutoExpand()) {
                buf.compact();
            } else {
                storeRemainingInSession(buf, session);
            }
        } else {
            if (usingSessionBuffer) {
                removeSessionBuffer(session);
            }
        }
    }

    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        // [消息头-长度][LONG][消息头-消息编号][消息体]
        // [short]     [long][short]           [...]

        // [消息头-长度]
        short length = (short) (in.getShort() - 2); // -2 是扣掉[消息头-长度]部分
        if (in.remaining() < length) {
            return false;
        }

        // [消息头-消息编号]
        in.getLong(); // long
        short code = in.getShort(); // [消息头-消息编号]

        // [消息体]
        byte[] bodyBytes = new byte[length - 10]; // -10 是扣掉[LONG][消息头-消息编号]部分
        in.get(bodyBytes);
        ByteArray byteArray = ByteArray.wrap(bodyBytes);
        AbstractDecoder decoder = MessageManager.getDecoder(code);
//        MessageWrapper messageWrap = new MessageWrapper(code, decoder.decode(byteArray));
        IStruct msg = decoder.decode(byteArray);

        // 输出解析消息
System.err.println("[解析消息]: " + JSON.toJSONString(msg)); // TODO 调试作用
        out.write(msg);
        return true;
    }

    @Override
    public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
    }

    @Override
    public void dispose(IoSession session) throws Exception {
        removeSessionBuffer(session);
    }

    private void removeSessionBuffer(IoSession session) {
        session.removeAttribute(BUFFER);
    }

    private void storeRemainingInSession(IoBuffer buf, IoSession session) {
        final IoBuffer remainingBuf = IoBuffer.allocate(buf.capacity()).setAutoExpand(true);

        remainingBuf.order(buf.order());
        remainingBuf.put(buf);

        session.setAttribute(BUFFER, remainingBuf);
    }
}
